package cn.com.easy.pay.weixinpay.controller.scan;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;

import cn.com.easy.dto.MessageDTO;
import cn.com.easy.exception.BusinessException;
import cn.com.easy.pay.weixinpay.config.WeixinPayConfig;
import cn.com.easy.utils.ResponseOutputUtils;

import com.tencent.business.ScanPayBusiness;
import com.tencent.business.ScanPayBusiness.ResultListener;
import com.tencent.common.Configure;
import com.tencent.protocol.pay_protocol.ScanPayReqData;
import com.tencent.protocol.pay_protocol.ScanPayResData;

/**
 * 收银端 刷卡支付控制器
 * 
 * @author nibili 2017年5月12日
 * 
 */
public abstract class WeixinPayCashierScanBaseController {

	private Logger logger = LoggerFactory.getLogger(WeixinPayCashierScanBaseController.class);

	/** 刷卡支付sdk,支付类 */
	private ScanPayBusiness scanPayBusiness;

	/**
	 * 获取微信支付配置<br/>
	 * 
	 * @param httpRequest
	 * @return
	 * @throws BusinessException
	 * @author nibili 2017年5月12日
	 */
	public abstract WeixinPayConfig getWeixinPayConfig(HttpServletRequest httpRequest) throws BusinessException;

	/**
	 * 支付参数<br/>
	 * 直接new ScanPayReqData(...)即可，无需再set，构造函数中，已设置 了必须的入参 <br/>
	 * 具体 参数，参考
	 * 
	 * @param httpRequest
	 * @return
	 * @author nibili 2017年5月8日
	 */
	public abstract ScanPayReqData getScanPayReqData(Configure configure, HttpServletRequest httpRequest) throws BusinessException;

	@RequestMapping("/request/pay")
	public void requestPay(final HttpServletRequest request, final HttpServletResponse response, String authCode) {
		try {

			WeixinPayConfig weixinPayConfig = getWeixinPayConfig(request);
			Configure configure = new Configure();
			configure.setKey(weixinPayConfig.getAppkey());
			configure.setAppID(weixinPayConfig.getAppid());
			configure.setMchID(weixinPayConfig.getMch_id());
			configure.setSubMchID(weixinPayConfig.getSubMchID());
			configure.setCertLocalPath(weixinPayConfig.getCertLocalPath());
			configure.setCertPassword(weixinPayConfig.getCertPassword());
			scanPayBusiness = new ScanPayBusiness(configure);
			//
			ScanPayReqData scanPayReqData = getScanPayReqData(configure, request);
			//
			scanPayBusiness.run(scanPayReqData, new ResultListener() {

				@Override
				public void onSuccess(ScanPayResData scanPayResData) {
					WeixinPayCashierScanBaseController.this.onSuccess(request, response, scanPayResData);
				}

				@Override
				public void onFailBySignInvalid(ScanPayResData scanPayResData) {
					WeixinPayCashierScanBaseController.this.onFailBySignInvalid(request, response, scanPayResData);
				}

				@Override
				public void onFailByReturnCodeFail(ScanPayResData scanPayResData) {
					WeixinPayCashierScanBaseController.this.onFailByReturnCodeFail(request, response, scanPayResData);
				}

				@Override
				public void onFailByReturnCodeError(ScanPayResData scanPayResData) {
					WeixinPayCashierScanBaseController.this.onFailByReturnCodeError(request, response, scanPayResData);
				}

				@Override
				public void onFailByMoneyNotEnough(ScanPayResData scanPayResData) {
					WeixinPayCashierScanBaseController.this.onFailByMoneyNotEnough(request, response, scanPayResData);
				}

				@Override
				public void onFailByAuthCodeInvalid(ScanPayResData scanPayResData) {
					WeixinPayCashierScanBaseController.this.onFailByAuthCodeInvalid(request, response, scanPayResData);
				}

				@Override
				public void onFailByAuthCodeExpire(ScanPayResData scanPayResData) {
					WeixinPayCashierScanBaseController.this.onFailByAuthCodeExpire(request, response, scanPayResData);
				}

				@Override
				public void onFail(ScanPayResData scanPayResData) {
					WeixinPayCashierScanBaseController.this.onFail(request, response, scanPayResData);
				}
			});
		} catch (BusinessException ex) {
			logger.error("刷卡支付发生异常(自定义异常):" + ex.getMessage(), ex);
			// 错误
			ResponseOutputUtils.renderJson(response, MessageDTO.newInstance("", false, ex.getMessage()));
		} catch (Exception ex) {
			logger.error("刷卡支付发生异常:" + ex.getMessage(), ex);
			// 错误
			ResponseOutputUtils.renderJson(response, MessageDTO.newInstance("", false, ex.getMessage()));
		}
	}

	/**
	 * API返回ReturnCode不合法，支付请求逻辑错误，请仔细检测传过去的每一个参数是否合法，或是看API能否被正常访问 <br/>
	 * 例：请检查每一个参数是否合法
	 * 
	 * @param request
	 * @param response
	 * @param scanPayResData
	 * @author nibili 2017年5月12日
	 */
	public abstract void onFailByReturnCodeError(HttpServletRequest request, HttpServletResponse response, ScanPayResData scanPayResData);

	/**
	 * API返回ReturnCode为FAIL，支付API系统返回失败，请检测Post给API的数据是否规范合法 <br/>
	 * 例 ： 请检测Post给API的数据是否规范合法
	 * 
	 * @param request
	 * @param response
	 * @param scanPayResData
	 * @author nibili 2017年5月12日
	 */
	public abstract void onFailByReturnCodeFail(HttpServletRequest request, HttpServletResponse response, ScanPayResData scanPayResData);

	/**
	 * 支付请求API返回的数据签名验证失败，有可能数据被篡改了<br/>
	 * 例：返回签名校验失败，此条数据疑似被篡改
	 * 
	 * @param request
	 * @param response
	 * @param scanPayResData
	 * @author nibili 2017年5月12日
	 */
	public abstract void onFailBySignInvalid(HttpServletRequest request, HttpServletResponse response, ScanPayResData scanPayResData);

	/**
	 * 用户用来支付的二维码已经过期，提示收银员重新扫一下用户微信“刷卡”里面的二维码;<br/>
	 * 例：二维码失效，请重新扫描
	 * 
	 * @param request
	 * @param response
	 * @param scanPayResData
	 * @author nibili 2017年5月12日
	 */
	public abstract void onFailByAuthCodeExpire(HttpServletRequest request, HttpServletResponse response, ScanPayResData scanPayResData);

	/**
	 * 授权码无效，提示用户刷新一维码/二维码，之后重新扫码支付<br/>
	 * 例：授权码无效，请重新扫描
	 * 
	 * @param request
	 * @param response
	 * @param scanPayResData
	 * @author nibili 2017年5月12日
	 */
	public abstract void onFailByAuthCodeInvalid(HttpServletRequest request, HttpServletResponse response, ScanPayResData scanPayResData);

	/**
	 * 用户余额不足，换其他卡支付或是用现金支付 <br/>
	 * 例：余额不足，换其他卡支付或是用现金支付
	 * 
	 * @param request
	 * @param response
	 * @param scanPayResData
	 * @author nibili 2017年5月12日
	 */
	public abstract void onFailByMoneyNotEnough(HttpServletRequest request, HttpServletResponse response, ScanPayResData scanPayResData);

	/**
	 * 支付失败<br/>
	 * 例：支付失败，请重扫描
	 * 
	 * @param request
	 * @param response
	 * @param scanPayResData
	 * @author nibili 2017年5月12日
	 */
	public abstract void onFail(HttpServletRequest request, HttpServletResponse response, ScanPayResData scanPayResData);

	/**
	 * 一次性支付成功<br
	 * / 例：支付成功
	 * 
	 * @param request
	 * @param response
	 * @param scanPayResData
	 * @author nibili 2017年5月12日
	 */
	public abstract void onSuccess(HttpServletRequest request, HttpServletResponse response, ScanPayResData scanPayResData);
}
